package cn.iocoder.yudao.module.crm.dal.mysql.customer;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.enums.QueryCondition;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.CrmCustomerPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueLabelDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLabelDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customfieldData.CustomFieldDataDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.util.*;

/**
 * 客户 Mapper
 *
 * @author Wanwan
 */
@Mapper
public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
    @Select("SELECT * FROM crm_customer WHERE name = #{name} AND module_id = #{moduleId} limit 1")
    CrmClueDO selectByNameAndModuleId(@Param("name") String name, @Param("moduleId") Long moduleId);

    @Select("SELECT * FROM crm_clue WHERE mobile = #{mobile} AND module_id = #{moduleId} limit 1")
    CrmClueDO selectByMobileAndModuleId(@Param("mobile") String mobile,@Param("moduleId") Long moduleId);
    default Long selectCountByLockStatusAndOwnerUserId(Boolean lockStatus, Long ownerUserId) {
        return selectCount(new LambdaUpdateWrapper<CrmCustomerDO>()
                .eq(CrmCustomerDO::getLockStatus, lockStatus)
                .eq(CrmCustomerDO::getOwnerUserId, ownerUserId));
    }

    default Long selectCountByDealStatusAndOwnerUserId(Long moduleId, @Nullable Boolean dealStatus, Long ownerUserId) {
        return selectCount(new LambdaQueryWrapperX<CrmCustomerDO>()
                .eqIfPresent(CrmCustomerDO::getDealStatus, dealStatus)
                .eqIfPresent(CrmCustomerDO::getModuleId, moduleId)
                .eq(CrmCustomerDO::getOwnerUserId, ownerUserId));
    }

    default Long selectCountByCreateTimeAndOwnerUserId(Long moduleId, LocalDateTime createTime, Long ownerUserId) {
        return selectCount(new LambdaQueryWrapperX<CrmCustomerDO>()
                .geIfPresent(CrmCustomerDO::getCreateTime, createTime)
                .eqIfPresent(CrmCustomerDO::getModuleId, moduleId)
                .eq(CrmCustomerDO::getOwnerUserId, ownerUserId));
    }

    default int updateOwnerUserIdById(Long id) {
        return update(new LambdaUpdateWrapper<CrmCustomerDO>()
                .eq(CrmCustomerDO::getId, id)
                .set(CrmCustomerDO::getOwnerUserId, null)
                .set(CrmCustomerDO::getIntoPoolTime, LocalDateTime.now())
        );
    }

    default PageResult<CrmCustomerDO> selectAdvancePage(PageParam pageReqVO, Long userId) {
        MPJLambdaWrapperX<CrmCustomerDO> queryWrapper = buildQueryMapper(pageReqVO);
        boolean isPool = false;
        if (Objects.nonNull(pageReqVO.getParams().get("pool"))) {
            isPool = Boolean.parseBoolean(pageReqVO.getParams().get("pool").get("value").toString());
        }
        // 1. 如果没有指定ownerUserId , 那么默认就是查自己的数据(我负责的)
        if (Objects.isNull(pageReqVO.getParams().get("owner_user_id"))) {
            // 拼接数据权限的查询条件
            CrmPermissionUtils.appendPermissionCondition(queryWrapper, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
                    CrmCustomerDO::getId,CrmCustomerDO::getOwnerUserId,  userId, CrmSceneTypeEnum.OWNER.getType(), isPool);
        } else {
            if (isPool) {
                queryWrapper.isNull(CrmCustomerDO::getOwnerUserId);
            }
        }
        // 标签需要特殊处理, 关联查询
        if (Objects.nonNull(pageReqVO.getParams().get("label_id"))) {
            String labelIds = pageReqVO.getParams().get("label_id").get("value").toString();
            queryWrapper.selectDistinctAll(CrmCustomerDO.class);
            queryWrapper.leftJoin(
                            CrmCustomerLabelDO.class, CrmCustomerLabelDO::getCustomerId, CrmCustomerDO::getId)
                    .in(CrmCustomerLabelDO::getLabelId, StrUtil.split(labelIds, ","));
        }
        buildSortFields(pageReqVO, queryWrapper);
        return selectJoinPage(pageReqVO, CrmCustomerDO.class, queryWrapper);
    }


    default PageResult<CrmCustomerDO> selectCustomAdvancePage(PageParam pageReqVO, Long userId) {
        Map<String, Map<String, Object>> params = new HashMap<>(pageReqVO.getParams());
        Map<String, Map<String, Object>> customParams = new HashMap<>();
        for (String key : params.keySet()) {
            Map<String, Object> stringObjectMap = params.get(key);
            for (String s : stringObjectMap.keySet()) {
                if (stringObjectMap.get("custom") != null && stringObjectMap.get("custom").toString().equals("custom")) {
                    customParams.put(key, stringObjectMap);
                }
            }
        }
        if (CollUtil.isNotEmpty(customParams)) {
            for (String key : customParams.keySet()) {
                pageReqVO.getParams().remove(key);
            }
        }
        MPJLambdaWrapperX<CrmCustomerDO> queryWrapper = buildQueryMapper(pageReqVO);
        boolean isPool = false;
        if (Objects.nonNull(pageReqVO.getParams().get("pool"))) {
            isPool = Boolean.parseBoolean(pageReqVO.getParams().get("pool").get("value").toString());
        }
        // 1. 如果没有指定ownerUserId , 那么默认就是查自己的数据(我负责的)
        if (Objects.isNull(pageReqVO.getParams().get("owner_user_id"))) {
            // 拼接数据权限的查询条件
            CrmPermissionUtils.appendPermissionCondition(queryWrapper, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
                    CrmCustomerDO::getId, CrmCustomerDO::getOwnerUserId, userId, CrmSceneTypeEnum.All.getType(), isPool);
        } else {
            if (isPool) {
                queryWrapper.isNull(CrmCustomerDO::getOwnerUserId);
            }
        }
        // 标签需要特殊处理, 关联查询
        if (Objects.nonNull(pageReqVO.getParams().get("label_id"))) {
//            String labelIds = pageReqVO.getParams().get("label_id").get("value").toString();
//            JSONArray objects = JSONUtil.parseArray(pageReqVO.getParams().get("label_id").get("value"));
//            queryWrapper.selectDistinctAll(CrmCustomerDO.class);
//            queryWrapper.leftJoin(
//                            CrmCustomerLabelDO.class, CrmCustomerLabelDO::getCustomerId, CrmCustomerDO::getId)
//                    .in(CrmCustomerLabelDO::getLabelId, objects);
            queryWrapper.selectDistinctAll(CrmCustomerDO.class);
            JSONArray objects = new JSONArray();
            String condition = pageReqVO.getParams().get("label_id").get("condition").toString();

            //等于
            if ("eq".equals(condition)) {
                String label = pageReqVO.getParams().get("label_id").get("value").toString();
                queryWrapper.leftJoin(
                                CrmCustomerLabelDO.class, CrmCustomerLabelDO::getCustomerId, CrmCustomerDO::getId)
                        .eq(CrmCustomerLabelDO::getLabelId, label);
            }
            //不等于
            else if ("neq".equals(condition)){
                String label = pageReqVO.getParams().get("label_id").get("value").toString();
                queryWrapper.leftJoin(
                                CrmCustomerLabelDO.class, CrmCustomerLabelDO::getCustomerId, CrmCustomerDO::getId)
                        .ne(CrmCustomerLabelDO::getLabelId, label);
            }
            //包含
            else if ("in".equals(condition)){
                objects = JSONUtil.parseArray(pageReqVO.getParams().get("label_id").get("value"));
                queryWrapper.leftJoin(
                                CrmCustomerLabelDO.class, CrmCustomerLabelDO::getCustomerId, CrmCustomerDO::getId)
                        .in(CrmCustomerLabelDO::getLabelId, objects);
            }
            //不包含
            else if ("nin".equals(condition)){
                String label = pageReqVO.getParams().get("label_id").get("value").toString();
                queryWrapper.leftJoin(
                                CrmCustomerLabelDO.class, CrmCustomerLabelDO::getCustomerId, CrmCustomerDO::getId)
                        .notIn(CrmCustomerLabelDO::getLabelId, label.split(","));
            }
            //为空
            else if ("isNull".equals(condition)){
//                queryWrapper.leftJoin(
//                                CrmClueLabelDO.class, CrmClueLabelDO::getClueId, CrmClueDO::getId)
//                        .isNull(CrmClueLabelDO::getLabelId);
                queryWrapper.leftJoin(CrmCustomerLabelDO.class,
                                (wrapper) -> wrapper.eq(CrmCustomerLabelDO::getCustomerId, CrmCustomerDO::getId))
                        .isNull(CrmCustomerLabelDO::getLabelId);;
            }
            //不为空
            else if ("isNotNull".equals(condition)){
                queryWrapper.leftJoin(
                                CrmCustomerLabelDO.class, CrmCustomerLabelDO::getCustomerId, CrmCustomerDO::getId)
                        .isNotNull(CrmCustomerLabelDO::getLabelId);
            }

        }
        customBuildQueryMapper(queryWrapper, customParams);
        buildSortFields(pageReqVO, queryWrapper);
        return selectJoinPage(pageReqVO, CrmCustomerDO.class, queryWrapper);
    }

    default void customBuildQueryMapper(MPJLambdaWrapperX<CrmCustomerDO> queryWrapper,Map<String, Map<String, Object>> customParams){
        if (CollUtil.isNotEmpty(customParams)) {
            queryWrapper.leftJoin(CustomFieldDataDO.class, "cu", CustomFieldDataDO::getDataId, CrmClueDO::getId);
            // 移除非本表字段的key, 然后在外层单独处理这个key
            // 遍历这个params
            StringBuilder applySql = new StringBuilder(" group by t.id HAVING 1 = 1");
            customParams.forEach((key, value) -> {
                Object val = value.get("value");
                String condition = value.get("condition").toString();
//                queryWrapper.groupBy(CrmClueDO::getId);
                switch (condition) {
                    case QueryCondition.EQ:
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date = '"+val+"' THEN 1 ELSE 0 END) > 0");
                        break;
                    case QueryCondition.NEQ:
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date != '"+val+"' THEN 1 ELSE 0 END) > 0");
                        break;
                    case QueryCondition.IN:
                        List<Object> list1 = JSONUtil.toList(JSONUtil.parseArray(val), Object.class);
                        StringBuilder sbu = new StringBuilder();
                        for (Object item : list1) {
                            if (sbu.length() > 0) {
                                sbu.append(",");
                            }
                            sbu.append(item.toString());
                        }
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date in ("+sbu.toString()+") THEN 1 ELSE 0 END) > 0");
                        break;
                    case QueryCondition.NIN:
                        List<Object> list2 = JSONUtil.toList(JSONUtil.parseArray(val), Object.class);
                        StringBuilder sbu2 = new StringBuilder();
                        for (Object item : list2) {
                            if (sbu2.length() > 0) {
                                sbu2.append(",");
                            }
                            sbu2.append(item.toString());
                        }
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date not in ("+sbu2.toString()+") THEN 1 ELSE 0 END) > 0");
                        break;
                    case QueryCondition.LIKE:
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date ike '%"+val+"%' THEN 1 ELSE 0 END) > 0");
                        break;
                    case QueryCondition.GT:
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date >'"+val+"' THEN 1 ELSE 0 END) > 0");
                        break;
                    case QueryCondition.GTE:
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date >='"+val+"' THEN 1 ELSE 0 END) > 0");
                        break;
                    case QueryCondition.LT:
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date <'"+val+"' THEN 1 ELSE 0 END) > 0");
                        break;
                    case QueryCondition.LTE:
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date <='"+val+"' THEN 1 ELSE 0 END) > 0");
                        break;
                    case QueryCondition.BETWEEN:
                        List<Object> list = JSONUtil.toList(JSONUtil.parseArray(val), Object.class);
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date >='"+list.get(0)+"' THEN 1 ELSE 0 END) > 0");
                        applySql.append(" and SUM(CASE WHEN cu.field_name = '" + key + "' AND cu.field_date <='"+list.get(1)+"' THEN 1 ELSE 0 END) > 0");
                        break;
                }
            });
            queryWrapper.last(applySql.toString());
        }
    };

    default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long ownerUserId) {
        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
        // 拼接数据权限的查询条件
        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
                CrmCustomerDO::getId, CrmCustomerDO::getOwnerUserId, ownerUserId, pageReqVO.getSceneType(), pageReqVO.getPool());
        // 拼接自身的查询条件

        query.selectAll(CrmCustomerDO.class)
                .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())
                .betweenIfPresent(CrmCustomerDO::getCreateTime, pageReqVO.getCreateTime())
                .betweenIfPresent(CrmCustomerDO::getContactNextTime, pageReqVO.getContactNextTime())
                .betweenIfPresent(CrmCustomerDO::getLastCallTime, pageReqVO.getLastCallTime())
                .betweenIfPresent(CrmCustomerDO::getLastAcceptTime, pageReqVO.getLastAcceptTime())
                // 添加放入公海时间
//                .betweenIfPresent(CrmCustomerDO::getIntoPoolTime, pageReqVO.getIntoPoolTime())
                .eqIfPresent(CrmCustomerDO::getMobile, pageReqVO.getMobile())
                .eqIfPresent(CrmCustomerDO::getIndustryId, pageReqVO.getIndustryId())
                .eqIfPresent(CrmCustomerDO::getLevelId, pageReqVO.getLevelId())
                .eqIfPresent(CrmCustomerDO::getSource, pageReqVO.getSource())
                .eqIfPresent(CrmCustomerDO::getFollowUpStatus, pageReqVO.getFollowUpStatus())
                .eqIfPresent(CrmCustomerDO::getLockStatus, pageReqVO.getLockStatus())
                .eqIfPresent(CrmCustomerDO::getDealStatus, pageReqVO.getDealStatus())
                .eqIfPresent(CrmCustomerDO::getOwnerUserId, pageReqVO.getOwnerUserId())
                .eqIfPresent(CrmCustomerDO::getModuleId, pageReqVO.getModuleId())
                .eqIfPresent(CrmCustomerDO::getDataType, pageReqVO.getDataType())
                .eqIfPresent(CrmCustomerDO::getAreaId, pageReqVO.getAreaId())
                .likeIfPresent(CrmCustomerDO::getDetailAddress, pageReqVO.getDetailAddress())
                // 领取时间
                .betweenIfPresent(CrmCustomerDO::getOwnerTime, pageReqVO.getOwnerTime())

        ;

        // 判断 label是否为空, 根据标签查询客户
        if (StrUtil.isNotEmpty(pageReqVO.getLabelId())) {
            query.leftJoin(CrmCustomerLabelDO.class, "lab", CrmCustomerLabelDO::getCustomerId, CrmCustomerDO::getId).in(CrmCustomerLabelDO::getLabelId, StrUtil.split(pageReqVO.getLabelId(), ","));
        }

        // 拼接排序条件
        buildSortFields(pageReqVO, query);

        // backlog 查询
        if (ObjUtil.isNotNull(pageReqVO.getContactStatus())) {
            Assert.isNull(pageReqVO.getPool(), "pool 必须是 null");
            LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
            LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
            if (pageReqVO.getContactStatus().equals(CrmCustomerPageReqVO.CONTACT_TODAY)) { // 今天需联系
                query.between(CrmCustomerDO::getContactNextTime, beginOfToday, endOfToday);
            } else if (pageReqVO.getContactStatus().equals(CrmCustomerPageReqVO.CONTACT_EXPIRED)) { // 已逾期
                query.lt(CrmCustomerDO::getContactNextTime, beginOfToday);
            } else if (pageReqVO.getContactStatus().equals(CrmCustomerPageReqVO.CONTACT_ALREADY)) { // 已联系
                query.between(CrmCustomerDO::getContactLastTime, beginOfToday, endOfToday);
            } else {
                throw new IllegalArgumentException("未知联系状态：" + pageReqVO.getContactStatus());
            }
        }
        return selectJoinPage(pageReqVO, CrmCustomerDO.class, query);
    }

    default List<CrmCustomerDO> selectBatchIds(Collection<Long> ids, Long ownerUserId) {
        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
        // 拼接数据权限的查询条件
        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, ownerUserId);
        // 拼接自身的查询条件
        query.selectAll(CrmCustomerDO.class).in(CrmCustomerDO::getId, ids).orderByDesc(CrmCustomerDO::getId);
        return selectJoinList(CrmCustomerDO.class, query);
    }

    default CrmCustomerDO selectByCustomerName(String name, Long moduleId, Long ownerUserId) {
        return selectOne(new LambdaQueryWrapperX<CrmCustomerDO>().eq(CrmCustomerDO::getName, name).eq(CrmCustomerDO::getOwnerUserId, ownerUserId).eq(CrmCustomerDO::getModuleId, moduleId), false);
    }


    default CrmCustomerDO selectByCustomerMobile(String mobile, Long moduleId, Long ownerUserId) {
        return selectOne(new LambdaQueryWrapperX<CrmCustomerDO>().eq(CrmCustomerDO::getOwnerUserId, ownerUserId).eq(CrmCustomerDO::getMobile, mobile).eq(CrmCustomerDO::getModuleId, moduleId), false);
    }


    default PageResult<CrmCustomerDO> selectPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO,
                                                                      CrmCustomerPoolConfigDO poolConfig,
                                                                      Long ownerUserId) {
        final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfig, ownerUserId);
        return selectJoinPage(pageReqVO, CrmCustomerDO.class, query.selectAll(CrmCustomerDO.class));
    }

    default Long selectPutPoolRemindCustomerCount(CrmCustomerPageReqVO pageReqVO,
                                                  CrmCustomerPoolConfigDO poolConfigDO,
                                                  Long userId) {
        final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfigDO, userId);
        return selectCount(query);
    }

    static MPJLambdaWrapperX<CrmCustomerDO> buildPutPoolRemindCustomerQuery(CrmCustomerPageReqVO pageReqVO,
                                                                            CrmCustomerPoolConfigDO poolConfig,
                                                                            Long ownerUserId) {
        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
        // 拼接数据权限的查询条件
        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
                CrmCustomerDO::getId, CrmCustomerDO::getOwnerUserId, ownerUserId, pageReqVO.getSceneType(), null);

        // 未锁定 + 未成交
        query.eq(CrmCustomerDO::getLockStatus, false).eq(CrmCustomerDO::getDealStatus, false);
        query.eq(CrmCustomerDO::getModuleId, poolConfig.getModuleId());
        // 情况一：未成交提醒日期区间
        Integer dealExpireDays = poolConfig.getDealExpireDays();
        LocalDateTime startDealRemindTime = LocalDateTime.now().minusDays(dealExpireDays);
        LocalDateTime endDealRemindTime = LocalDateTime.now()
                .minusDays(Math.max(dealExpireDays - poolConfig.getNotifyDays(), 0));
        // 情况二：未跟进提醒日期区间
        Integer contactExpireDays = poolConfig.getContactExpireDays();
        LocalDateTime startContactRemindTime = LocalDateTime.now().minusDays(contactExpireDays);
        LocalDateTime endContactRemindTime = LocalDateTime.now()
                .minusDays(Math.max(contactExpireDays - poolConfig.getNotifyDays(), 0));
        query.and(q -> {
            // 情况一：成交超时提醒
            q.between(CrmCustomerDO::getOwnerTime, startDealRemindTime, endDealRemindTime)
                    // 情况二：跟进超时提醒
                    .or(w -> w.between(CrmCustomerDO::getOwnerTime, startContactRemindTime, endContactRemindTime)
                            .and(p -> p.between(CrmCustomerDO::getContactLastTime, startContactRemindTime, endContactRemindTime)
                                    .or().isNull(CrmCustomerDO::getContactLastTime)));
        });
        return query;
    }

    /**
     * 获得需要过期到公海的客户列表
     *
     * @return 客户列表
     */
    default List<CrmCustomerDO> selectListByAutoPool(CrmCustomerPoolConfigDO poolConfig) {
        LambdaQueryWrapper<CrmCustomerDO> query = new LambdaQueryWrapper<>();
        query.gt(CrmCustomerDO::getOwnerUserId, 0);
        // 未锁定 + 未成交
        query.eq(CrmCustomerDO::getLockStatus, false).eq(CrmCustomerDO::getDealStatus, false);
        // 指定模块
        query.eq(CrmCustomerDO::getModuleId, poolConfig.getModuleId());
        // 已经超时
        LocalDateTime dealExpireTime = LocalDateTime.now().minusDays(poolConfig.getDealExpireDays());
        LocalDateTime contactExpireTime = LocalDateTime.now().minusDays(poolConfig.getContactExpireDays());
        query.and(q -> {
            // 情况一：成交超时
            q.lt(CrmCustomerDO::getOwnerTime, dealExpireTime)
                    // 情况二：跟进超时
                    .or(w -> w.lt(CrmCustomerDO::getOwnerTime, contactExpireTime)
                            .and(p -> p.lt(CrmCustomerDO::getContactLastTime, contactExpireTime)
                                    .or().isNull(CrmCustomerDO::getContactLastTime)));
        });
        return selectList(query);
    }

    default Long selectCountByTodayContact(Long ownerUserId) {
        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
        // 我负责的 + 非公海
        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
                CrmCustomerDO::getId, CrmCustomerDO::getOwnerUserId, ownerUserId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
        // 今天需联系
        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
        LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
        query.between(CrmCustomerDO::getContactNextTime, beginOfToday, endOfToday);
        return selectCount(query);
    }

    default Long selectCountByOverdueContact(Long ownerUserId) {
        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
        // 我负责的 + 非公海
        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
                CrmCustomerDO::getId, CrmCustomerDO::getOwnerUserId, ownerUserId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
        // 逾期未联系
        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
        query.lt(CrmCustomerDO::getContactNextTime, beginOfToday);
        // 反复思考了这个问题， 为什么不需要判断 上次联系时间 和 下次联系时间的 大小关系呢？， 因为在web端，想要创建跟进（联系）这个表单内 下次联系时间是必填的，也就是说
        // 下次联系时间如果小于今天，那么在今天之前，肯定是没有新建联系（跟进）的；
        // 但是问题来了， 通过api回调（外呼）更新的上次跟进记录， 是没有下次联系时间这个数值的， 所以需要额外追加一个查询条件， 上次联系时间 小于 下次联系时间的。
        // 补充： call api 自动创建的跟进， 下次联系时间是空的， 所以下面的这个判断也就没用了。
//        query.lt(CrmCustomerDO::getContactLastTime, CrmCustomerDO::getContactNextTime);
        return selectCount(query);
    }

    default Long selectCountByFollow(Long ownerUserId) {
        MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
        // 我负责的 + 非公海
        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
                CrmCustomerDO::getId, CrmCustomerDO::getOwnerUserId, ownerUserId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE);
        // 未跟进
        query.eq(CrmClueDO::getFollowUpStatus, false);
        return selectCount(query);
    }

}
